﻿//////////////////////////////////////////////////////////////////
//
// Formatter.cs
//
// Copyright (c) 2012 Dan Pike.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation files
// (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Utilities
{
   public class Formatter
   {
      ////////////////////////////////////////////////////////////////////////////////////////
      //
      // |00000000   00 00 00 00   00 00 00 00   00 00 00 00   00 00 00 00   ................|
      //  ^         ^         ^         ^         ^         ^         ^         ^         ^   
      public static string HexPanel(List<ArraySegment<byte>> data)
      {
         if ((null == data) || (0 == data.Count))
         {
            return String.Empty;
         }

         StringBuilder sb = new StringBuilder();
         ArraySegment<byte> thisSeg = data[0];
         int row = 0, col = 0, segIndex = 1, byteIndex = thisSeg.Offset;

         // Get a scratch-pad to build each row
         char[] rowText = Enumerable.Repeat(' ', 76).ToArray();
         rowText[rowText.Length - 1] = '\n';

         while (true)
         {
            // Get the next byte in the array of segments
            if (byteIndex >= thisSeg.Offset + thisSeg.Count)
            {
               if (segIndex >= data.Count)
               {
                  // End of the list
                  if (0 < col)
                  {
                     // add the final row
                     for (int colIndex = 3 + (3 * col) + 2 * (col / 4); colIndex < 59; ++colIndex)
                     {
                        rowText[colIndex] = ' ';
                     }
                     while (17 > col)
                     {
                        rowText[59 + col++] = ' ';
                     }
                     sb.Append(rowText);
                  }
                  break;
               }
               thisSeg = data[segIndex++];
               byteIndex = thisSeg.Offset;
               continue;
            }
            byte thisByte = ((byte[])thisSeg.Array)[byteIndex++];

            if (0 == (col % 16))
            {
               // Should we move on to a new row?
               if (0 < row)
               {
                  sb.Append(rowText);
                  if (0 == (row % 64))
                  {
                     sb.Append('\n');
                  }
               }
               sb.Append("    ");
               sb.Append(row.ToString("X").PadLeft(8, '0'));
               col = 0;
               row += 16;
            }

            rowText[4 + (3 * col) + 2 * (col / 4)] = (char)((10 > (thisByte & 0xf)) ? (thisByte & 0xf) + '0' : (thisByte & 0xf) - 10 + 'A');
            rowText[3 + (3 * col) + 2 * (col / 4)] = (char)((10 > (thisByte >> 4)) ? (thisByte >> 4) + '0' : (thisByte >> 4) - 10 + 'A');

            char thisValue = (char)(thisByte & 0x7f);
            if (Char.IsControl((char)thisValue))
            {
               thisValue = '.';
            }
            rowText[59 + col] = thisValue;
            ++col;
         }
         return sb.ToString();
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public static string HexPanel(ArraySegment<byte> data)
      {
         List<ArraySegment<byte>> dump = new List<ArraySegment<byte>>(1);
         dump.Add(data);
         return HexPanel(dump);
      }

      //////////////////////////////////////////////////////////////////////////
      //
      public static string HexPanel(byte[] data)
      {
         if ((null == data) || (0 == data.Length))
         {
            return "";
         }
         List<ArraySegment<byte>> dump = new List<ArraySegment<byte>>(1);
         dump.Add(new ArraySegment<byte>(data));
         return HexPanel(dump);
      }

      ////////////////////////////////////////////////////////////////////////////////////////
      //
      public static string StringList(List<string> words, int wordsPerLine, string lineIndent)
      {
         if ((null == words) || (0 >= words.Count))
         {
            return "";
         }

         bool firstWord = true;
         StringBuilder sb = new StringBuilder((lineIndent.Length + 24) * words.Count);
         int digits = (int)Math.Ceiling(Math.Log10(words.Count));
         string lineIndexFormat = string.Format("{{0,{0}}}: ", digits);
         
         int lineIndex = 0;
         int wordsThisLine = 0;
         foreach (string word in words)
         {
            if (firstWord)
            {
               firstWord = false;
               if (0 < wordsPerLine)
               {
                  if (0 < lineIndex)
                  {
                     sb.Append('\n');
                  }
                  sb.Append(lineIndent);
                  sb.AppendFormat(lineIndexFormat, lineIndex);
               }
            }
            else
            {
               sb.Append(", ");
            }
            if (null == word)
            {
               sb.Append("(null)");
            }
            else
            {
               sb.Append('\'');
               sb.Append(Regex.Escape(word));
               sb.Append('\'');
            }
            if ((0 < wordsPerLine) && (wordsPerLine <= ++wordsThisLine))
            {
               ++lineIndex;
               firstWord = true;
               wordsThisLine = 0;
            }
         }
         return sb.ToString();
      }

      ////////////////////////////////////////////////////////////////////////////////////////
      //
      public static string Exception(Exception ex)
      {
         StringBuilder sb = new StringBuilder();
         StringBuilder indent = new StringBuilder("      ");
         for (Exception thisEx = ex; null != thisEx; thisEx = thisEx.InnerException)
         {
            sb.Append(indent);
            sb.Append("> ");
            sb.Append(thisEx.Message);
            sb.Append("\n");
            indent.Append("...");
         }

         StackTrace trace = new StackTrace(ex, true);
         foreach (StackFrame frame in trace.GetFrames())
         {
            string filename = frame.GetFileName();
            int lineNumber = frame.GetFileLineNumber();

            // Output the file name if we have it
            sb.Append("   ");
            if (!string.IsNullOrWhiteSpace(filename))
            {
               sb.Append(filename);
               if (0 < lineNumber)
               {
                  sb.Append('(');
                  sb.Append(lineNumber);
                  sb.Append(')');
               }
               sb.Append(" : ");
            }
            else
            {
               sb.Append("<file information not available> : ");
            }

            // Output the method name
            MethodBase method = frame.GetMethod();
            sb.Append(method.Name);
            sb.Append('(');
            bool firstParam = true;
            foreach (ParameterInfo info in method.GetParameters())
            {
               if (!firstParam)
               {
                  sb.Append(", ");
               }
               firstParam = false;

               sb.Append(info.ParameterType.Name);
               sb.Append(' ');
               sb.Append(info.Name);
            }
            sb.Append(")\r\n");
         }
         return sb.ToString();
      }

      ////////////////////////////////////////////////////////////////////////////////////////
      //
      public static string OrderStatusString(string ordStatus)
      {
         switch (ordStatus)
         {
            case "0": return "0(New)";
            case "1": return "1(Partially filled)";
            case "2": return "2(Filled)";
            case "3": return "3(Done for day)";
            case "4": return "4(Canceled)";
            case "5": return "5(Replaced)";
            case "6": return "6(Pending Cancel )";
            case "7": return "7(Stopped)";
            case "8": return "8(Rejected)";
            case "9": return "9(Suspended)";
            case "A": return "A(Pending New)";
            case "B": return "B(Calculated)";
            case "C": return "C(Expired)";
            case "D": return "D(Accepted for Bidding)";
            case "E": return "E(Pending Replace)";
            
            default:
               break;
         }
         return string.Format("{0}(???)", ordStatus);
      }
   }
}
